#pragma once

#include "params.hpp"
#include "types.h"
#include <cstdint>
#include <functional>
#include <time.h>

namespace Trade {

enum class TRIGGER_EVENT_TYPE : uint32_t
{
  NONE,
  MARK_PRICE_UP,
  MARK_PRICE_DN,
  MARK_TIMER,
  OPEN,
  CLOSE,
  CLOSE_MIN_WIN,
  CLOSE_STOP_WIN,
  CLOSE_STOP_LOSS,
  MAX_EVENT_CODE
};

enum class TRIGGER_TYPE : uint32_t
{
  NONE,
  SPACE,
  TIME,
};

struct MTParams
{
  dir_t dir_tjm = 0;
};

struct Trigger;
using trigger_call_back = std::function<int(const Trigger*)>;

struct MoneyTree;

struct Trigger
{
  Trigger()
    : hp_err(3)
    , tm_err(15 * 60)
  {
  }
  ~Trigger() = default;

  void set_price_trigger(const dir_t d,
                         const price_t hpx,
                         const TRIGGER_EVENT_TYPE why,
                         const Params* vo,
                         const MoneyTree* mt,
                         const DATAPTR userdata) noexcept;

  void set_timer_trigger(const time_t tm,
                         const TRIGGER_EVENT_TYPE why,
                         const Params* vo,
                         const MoneyTree* mt,
                         const DATAPTR userdata) noexcept;
  void set_call_back(trigger_call_back tdb) noexcept;
  void set_error(const price_t hp_err, const int tm_err) noexcept;

  TRIGGER_TYPE pull_trigger(const price_t hpx, const time_t tm) noexcept;
  TRIGGER_TYPE is_triggered() const noexcept;
  void clear() noexcept;

  dir_t dir = 0;          // for waiting for space, > 0 for above, < 0 for below
  bool triggered = false; // triggered or not
  TRIGGER_EVENT_TYPE etype = TRIGGER_EVENT_TYPE::NONE;
  TRIGGER_TYPE ttype = TRIGGER_TYPE::NONE;
  time_t tm = 0;
  time_t reached_tm = 0;
  price_t hpx = 0;
  price_t reached_hpx = 0;
  price_t hp_err = 0;
  int tm_err = 0;
  trigger_call_back trigger_cb = nullptr;
  DATAPTR userdata = nullptr;
  Params params;
  MTParams mtp;
};

struct TriggerManager
{
  Trigger ta[int(TRIGGER_EVENT_TYPE::MAX_EVENT_CODE) * 2];
  Trigger* ta_triggered[int(TRIGGER_EVENT_TYPE::MAX_EVENT_CODE) * 2];

  void set_price_trigger(const dir_t d,
                         const price_t hpx,
                         const TRIGGER_EVENT_TYPE why,
                         const Params* vo = nullptr,
                         const MoneyTree* mt = nullptr,
                         const DATAPTR userdata = nullptr) noexcept;
  void set_timer_trigger(const time_t tm,
                         const TRIGGER_EVENT_TYPE why,
                         const Params* vo = nullptr,
                         const MoneyTree* mt = nullptr,
                         const DATAPTR userdata = nullptr) noexcept;
  int pull_trigger(const price_t hpx, const time_t tm) noexcept;
  TRIGGER_TYPE is_triggered(const TRIGGER_EVENT_TYPE why,
                            const TRIGGER_TYPE ttype) noexcept;
  Trigger* get_trigger(const TRIGGER_EVENT_TYPE why,
                       const TRIGGER_TYPE ttype) noexcept;

  void set_mark_price_up(const price_t hpx,
                         const Params* vo = nullptr,
                         const MoneyTree* mt = nullptr,
                         const DATAPTR userdata = nullptr) noexcept;
  void set_mark_price_dn(const price_t hpx,
                         const Params* vo = nullptr,
                         const MoneyTree* mt = nullptr,
                         const DATAPTR userdata = nullptr) noexcept;
  void set_timer(const time_t tm,
                 const Params* vo = nullptr,
                 const MoneyTree* mt = nullptr,
                 const DATAPTR userdata = nullptr) noexcept;
  void unset_timer() noexcept;
  void unset_mark_price_up() noexcept;
  void unset_mark_price_dn() noexcept;

  void set_open_price(const dir_t d,
                      const price_t good_to_open,
                      const Params* vo = nullptr,
                      const MoneyTree* mt = nullptr,
                      const DATAPTR userdata = nullptr) noexcept;
  void unset_open_price() noexcept;

  void set_close_price(const dir_t d,
                       const price_t good_to_win,
                       const Params* vo = nullptr,
                       const MoneyTree* mt = nullptr,
                       const DATAPTR userdata = nullptr) noexcept;
  void set_close_stop_loss(const dir_t d,
                           const price_t stop_loss,
                           const Params* vo = nullptr,
                           const MoneyTree* mt = nullptr,
                           const DATAPTR userdata = nullptr) noexcept;
  void set_close_stop_win(const dir_t d,
                          const price_t max_win,
                          const Params* vo = nullptr,
                          const MoneyTree* mt = nullptr,
                          const DATAPTR userdata = nullptr) noexcept;
  void set_close_min_win(const dir_t d,
                         const price_t min_win,
                         const Params* vo = nullptr,
                         const MoneyTree* mt = nullptr,
                         const DATAPTR userdata = nullptr) noexcept;
  void unset_close_price() noexcept;
  void unset_close_stop_loss() noexcept;
  void unset_close_stop_win() noexcept;
  void unset_close_min_win() noexcept;
  void unset_all_close() noexcept;

  void print_close_triggers(const price_t hp_now) const noexcept;

  price_t get_close_price_stop_loss() noexcept;
  price_t get_close_price_stop_win() noexcept;
  price_t get_close_price_min_win() noexcept;
  price_t get_close_price() noexcept;

  void unset_all() noexcept;
};

} // namespace Trade